---
title: Astro 中的 Markdown
description: 了解 Astro 中内置的 Markdown 支持。
sidebar:
  label: Markdown
i18nReady: true
---

import Since from '~/components/Since.astro';
import { FileTree } from '@astrojs/starlight/components';
import RecipeLinks from "~/components/RecipeLinks.astro";
import ReadMore from '~/components/ReadMore.astro';
import { Steps } from '@astrojs/starlight/components';

[Markdown](https://daringfireball.net/projects/markdown/) 通常用于创作文本繁重的内容，如博客文章和文档。Astro 包括对 Markdown 文件的内置支持，这些文件还可以包括 [frontmatter YAML](https://dev.to/paulasantamaria/introduction-to-yaml-125f)（或 [TOML](https://toml.io)）来定义自定义属性，例如标题、描述和标签。

在 Astro 中，你可以在 [GitHub 风格的 Markdown](https://github.github.com/gfm/) 中创作内容，然后在 `.astro` 组件中渲染它。它结合了为内容设计熟悉的书写格式、 Astro 组件语法以及架构的灵活性。

:::tip
有关其他功能，例如在 Markdown 中包含组件和 JSX 表达式，请添加 [`@astrojs/mdx`](/zh-cn/guides/integrations-guide/mdx/) 集成以使用 [MDX](https://mdxjs.com/) 编写 Markdown 内容。
:::

## 组织 Markdown 文件

你的本地 Markdown 文件可以保存在 `src/` 目录中的任何位置。在 `src/pages/` 目录下的 Markdown 文件将会自动生成 [Markdown 页面于你的网页上](#单独的-markdown-页面)。

你的 Markdown 内容和 frontmatter 属性都可以通过 [本地文件导入](#导入-markdown) 来在组件中加以使用，或者当 [使用内容集合助手函数来查询和渲染数据请求](#来自内容集合查询的-markdown) 时也能使用。

### 文件导入 vs 内容集合查询

要将单个本地的 Markdown 文件的导入到 `.astro` 组件，可以使用 `import` 语句来实现，而要想一次性查询多个文件，则可以使用 Vite 的 [`import.meta.glob()`](/zh-cn/guides/imports/#importmetaglob)。[从这些 Markdown 文件中导出的数据](#导入-markdown) 都可以在 `.astro` 组件中使用。

如果你有一组相关的 Markdown 文件，可以考虑 [将它们定义为集合](/zh-cn/guides/content-collections/)。这将为你提供一些便利，其中包括了能够将 Markdown 文件存储在文件系统上的任何位置或使用远程存储。

集合使用专注于内容的优化 API 来 [查询和渲染你的 Markdown 内容](#来自内容集合查询的-markdown)，这种方式代替了文件导入。集合适用于共享相同结构的数据集，例如博客文章或产品项。当你在 schema 中定义集合时，还可以在编辑器中获得验证、类型安全和智能提示。

<ReadMore>了解更多有关 [什么时候使用内容集合](/zh-cn/guides/content-collections/#何时创建集合) 来代替文件导入。</ReadMore>

## 类 JSX 的动态表达式

导入或查询 Markdown 文件后，你可以在 `.astro` 组件中编写包含 frontmatter 数据和正文内容的动态 HTML 模板。

```md title="src/pages/posts/great-post.md"
---
title: '有史以来最好的文章'
author: 'Ben'
---

这是我的 _很棒_ 的文章！
```

```astro title="src/pages/my-posts.astro"
---
import * as greatPost from './posts/great-post.md';
const posts = Object.values(import.meta.glob('./posts/*.md', { eager: true }));
---

<p>{greatPost.frontmatter.title}</p>
<p>作者：{greatPost.frontmatter.author}</p>

<p>文章归档：</p>
<ul>
  {posts.map(post => <li><a href={post.url}>{post.frontmatter.title}</a></li>)}
</ul>
```

### 可用属性

#### 来自内容集合查询的 Markdown

当你从自己的集合使用助手函数 `getCollection()` 或 `getEntry()` 来请求数据时，你的 Markdown 的 frontmatter 属性都在一个可用的 `data` 对象之中（例如 `post.data.title`）。此外，`body` 包含了 Markdown 的正文内容，这部分内容是原始的、未编译的字符串类型。

[`render()`](/zh-cn/reference/modules/astro-content/#render) 函数将返回你的 Markdown 正文内容、生成的标题列表，以及应用了 remark 或 rehype 插件修改之后的 frontmatter 对象。


<ReadMore>详细了解 [使用集合查询返回的内容](/zh-cn/guides/content-collections/#在-astro-模板中使用内容)。</ReadMore>

#### 导入 Markdown

当使用 `import` 或 `import.meta.glob()` 导入 Markdown 时，以下导出的属性在你的 `.astro` 组件中可用：

- **`file`** - 绝对文件路径（例如 `/home/user/projects/.../file.md`）。
- **`url`** - 如果是页面，则为页面的 URL（例如 `/zh-cn/guides/markdown-content`）。
- **`frontmatter`** - 包含了文件的 YAML frontmatter 中所指定的任何数据。
- **`<Content />`** - 返回文件完整渲染内容的组件。
- **`rawContent()`** - 一个函数，将原始 Markdown 文档作为字符串返回。
- **`compiledContent()`** - 一个异步函数，将 Markdown 文档编译为 HTML 字符串后返回。
- **`getHeadings()`** - 一个异步函数，用于返回文件中所有标题（`<h1>` 到 `<h6>`）的数组，类型为：`{ depth: number; slug: string; text: string }[]`。每个标题的 `slug` 都对应了给定标题生成的 ID，可用于锚点链接。

示例 Markdown 博客文章可能会传递以下 `Astro.props` 对象：

```js
Astro.props = {
  file: "/home/user/projects/.../file.md",
  url: "/en/guides/markdown-content/",
  frontmatter: {
    /** 从博客文章中获取的 Frontmatter */
    title: "Astro 0.18 Release",
    date: "Tuesday, July 27 2021",
    author: "Matthew Phillips",
    description: "Astro 0.18 is our biggest release since Astro launch.",
  },
  getHeadings: () => [
    {"depth": 1, "text": "Astro 0.18 Release", "slug": "astro-018-release"},
    {"depth": 2, "text": "Responsive partial hydration", "slug": "responsive-partial-hydration"}
    /* ... */
  ],
  rawContent: () => "# Astro 0.18 Release\nA little over a month ago, the first public beta [...]",
  compiledContent: () => "<h1>Astro 0.18 Release</h1>\n<p>A little over a month ago, the first public beta [...]</p>",
}
```

## `<Content />` 组件

`<Content />` 组件可通过从 Markdown 文件导入 `Content` 来使用。此组件返回文件的完整正文内容，并渲染为 HTML。你可以选择将 `Content` 重命名为你喜欢的任何组件名称。

同样，你也可以通过渲染 `<Content />` 组件来 [渲染 Markdown 集合条目的 HTML 内容](/zh-cn/guides/content-collections/#渲染正文内容)。

```astro title="src/pages/content.astro" "Content"
---
// 导入语句
import {Content as PromoBanner} from '../components/promoBanner.md';

// 集合查询
import { getEntry, render } from 'astro:content';

const product = await getEntry('products', 'shirt');
const { Content } = await render(product);
---
<h2>今日促销</h2>
<PromoBanner />

<p>促销截止至：{product.data.saleEndDate.toDateString()}</p>
<Content />
```

## 标题 ID

在 Markdown 中编写标题会自动为你提供锚点链接，以便你可以直接跳转到页面的某些部分。

```markdown title="src/pages/page-1.md"
---
title: 我的文章目录
---
## 简介

当我使用 Markdown 时，我可以在同一个页面内链接到 [我的结论](#结论) 部分。

## 结论

我可以通过在浏览器访问 `https://example.com/page-1/#简介` 以直接导航到简介部分。
```

Astro 基于 `github-slugger` 生成标题 `id`。你可以在 [github-slugger 文档](https://github.com/Flet/github-slugger#usage) 中找到更多示例。

### 标题 ID 与插件

Astro 将 `id` 属性注入到 Markdown 和 MDX 文件中的所有标题元素（`<h1>` 到 `<h6>`）中。当你通过文件导入的方式来 [导出 Markdown 的属性](#可用属性) 时，或者在 [使用从内容集合查询返回的 Markdown 内容](#来自内容集合查询的-markdown) 时，你都可以从 `getHeadings()` 工具中检索到这些数据。

你可以通过添加注入 `id` 属性的 rehype 插件（例如 `rehype-slug`）来自定义这些标题 ID。你的自定义 ID 将替代 Astro 的默认 ID，反映在 HTML 输出和 `getHeadings()` 返回的数组中。

默认情况下，Astro 会在你的 rehype 插件运行后注入 `id` 属性。但如果其中一个自定义 rehype 插件需要访问 Astro 注入的 ID，你可以直接导入并使用 Astro 的 `rehypeHeadingIds` 插件。确保在任何依赖它的插件之前添加 `rehypeHeadingIds`：

```js title="astro.config.mjs" ins={2, 8}
import { defineConfig } from 'astro/config';
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
import { otherPluginThatReliesOnHeadingIDs } from 'some/plugin/source';

export default defineConfig({
  markdown: {
    rehypePlugins: [
      rehypeHeadingIds,
      otherPluginThatReliesOnHeadingIDs,
    ],
  },
});
```

## Markdown 插件

Astro 中的 Markdown 支持由 [remark](https://remark.js.org/) 提供，remark 是一个强大的解析和处理工具，拥有一个活跃的生态系统。目前不支持其他 Markdown 解析器，如 Pandoc 和 markdown-it。

Astro 默认应用 [GitHub 风格的 Markdown](https://github.com/remarkjs/remark-gfm) 和 [SmartyPants](https://github.com/silvenon/remark-smartypants) 插件。这带来了一些便利，例如从文本生成可点击的链接，并格式化[引用和 em-dashes](https://daringfireball.net/projects/smartypants/)。

你可以在 `astro.config.mjs` 中自定义 remark 解析 Markdown 的方式。请参阅完整的 [Markdown 配置选项](/zh-cn/reference/configuration-reference/#markdown-选项)列表。

### 添加 remark 和 rehype 插件

Astro 支持添加第三方 [remark](https://github.com/remarkjs/remark) 和 [rehype](https://github.com/rehypejs/rehype) 插件来解析 Markdown。这些插件允许你扩展你的 Markdown，以获得新的功能，例如 [自动生成目录](https://github.com/remarkjs/remark-toc)，[应用可访问的表情符号标签](https://github.com/florianeckerstorfer/remark-a11y-emoji)，以及[为你的 Markdown 添加样式](/zh-cn/guides/styling/#markdown-样式)。

我们鼓励你浏览 [awesome-remark](https://github.com/remarkjs/awesome-remark) 和 [awesome-rehype](https://github.com/rehypejs/awesome-rehype) 来查看流行的插件！请参阅每个插件自己的 README 以获取特定的安装说明。

这个例子将 [`remark-toc`](https://github.com/remarkjs/remark-toc) 和 [`rehype-accessible-emojis`](https://www.npmjs.com/package/rehype-accessible-emojis) 应用于 Markdown 文件：

```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import remarkToc from 'remark-toc';
import { rehypeAccessibleEmojis } from 'rehype-accessible-emojis';

export default defineConfig({
  markdown: {
    remarkPlugins: [ [remarkToc, { heading: 'toc', maxDepth: 3 } ] ],
    rehypePlugins: [rehypeAccessibleEmojis],
  },
});
```

#### 自定义插件

为了自定义插件，请在嵌套数组中提供选项对象。

下面的示例将 [标题选项添加到 `remarkToc` 插件](https://github.com/remarkjs/remark-toc#options) 以更改目录的放置位置，并将 [`behavior` 选项添加到 `rehype-autolink-headings` 插件](https://github.com/rehypejs/rehype-autolink-headings#options)以便在标题文本之后添加锚标记。

```js title="astro.config.mjs"
import remarkToc from 'remark-toc';
import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';

export default {
  markdown: {
    remarkPlugins: [ [remarkToc, { heading: "contents"} ] ],
    rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: 'append' }]],
  },
}
```

### 以编程方式修改 frontmatter

你可以通过使用 [remark 或 rehype 插件](#markdown-插件) 为所有 Markdown 和 MDX 文件添加 frontmatter 属性。

<Steps>
1. 从插件的 `file` 参数中将 `customProperty`  附加到 `data.astro.frontmatter` 属性：

    ```js title="example-remark-plugin.mjs"
    export function exampleRemarkPlugin() {
      // 所有 remark 和 rehype 插件都返回一个单独的函数
      return function (tree, file) {
        file.data.astro.frontmatter.customProperty = 'Generated property';
      }
    }
    ```

    :::tip
    <Since v="2.0.0" />

    `data.astro.frontmatter` 包含给定 Markdown 或 MDX 文档的所有属性。这允许你修改现有的 frontmatter 属性，或者从现有的 frontmatter 计算新属性。
    :::  

2. 将此插件应用于你的 `markdown` 或 `mdx` 集成配置：

    ```js title="astro.config.mjs" "import { exampleRemarkPlugin } from './example-remark-plugin.mjs';" "remarkPlugins: [exampleRemarkPlugin],"
    import { defineConfig } from 'astro/config';
    import { exampleRemarkPlugin } from './example-remark-plugin.mjs';

    export default defineConfig({
      markdown: {
        remarkPlugins: [exampleRemarkPlugin]
      },
    });
    ```

    或者

    ```js title="astro.config.mjs" "import { exampleRemarkPlugin } from './example-remark-plugin.mjs';" "remarkPlugins: [exampleRemarkPlugin],"
    import { defineConfig } from 'astro/config';
    import { exampleRemarkPlugin } from './example-remark-plugin.mjs';

    export default defineConfig({
      integrations: [
        mdx({
          remarkPlugins: [exampleRemarkPlugin],
        }),
      ],
    });
    ```
</Steps>

现在，每个 Markdown 或 MDX 文件都会在其 frontmatter 中有 `customProperty`，使其在 [导入你的 markdown](#导入-markdown) 时以及从 [布局中的 `Astro.props.frontmatter` 属性](#frontmatter-layout-属性) 中可用。

<RecipeLinks slugs={["zh-cn/recipes/reading-time"]} />

### 从 MDX 扩展 Markdown 配置

Astro 的 MDX 集成默认情况下会扩展[你的项目的现有 Markdown 配置](/zh-cn/reference/configuration-reference/#markdown-选项)。要覆盖单个选项，你可以在 MDX 配置中指定它们的等效项。

下面的示例禁用了 GitHub-Flavored Markdown，并为 MDX 文件应用了不同的 remark 插件集：

```ts title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';

export default defineConfig({
  markdown: {
    syntaxHighlight: 'prism',
    remarkPlugins: [remarkPlugin1],
    gfm: true,
  },
  integrations: [
    mdx({
      // `syntaxHighlight` 继承自 Markdown
      // Markdown `remarkPlugins` 被忽略
      // 仅应用 `remarkPlugin2`
      remarkPlugins: [remarkPlugin2],
      // `gfm` 覆盖为 `false`
      gfm: false,
    })
  ]
});
```

要避免从 MDX 扩展你的 Markdown 配置，请将 [`extendMarkdownConfig` 选项](/zh-cn/guides/integrations-guide/mdx/#extendmarkdownconfig) (默认开启) 设置为 `false`:

```ts title="astro.config.mjs"
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';

export default defineConfig({
  markdown: {
    remarkPlugins: [remarkPlugin],
  },
  integrations: [
    mdx({
      // Markdown 配置现在被忽略
      extendMarkdownConfig: false,
      // 没有 `remarkPlugins` 应用
    })
  ]
});
```

## 单独的 Markdown 页面

:::tip
[内容集合](/zh-cn/guides/content-collections/) 和 [将 Markdown 导入 .astro 组件](#类-jsx-的动态表达式) 提供了更多用于渲染 Markdown 的功能，并且是处理大部分内容的推荐方法。然而，有时你可能也希望更便捷的只添加单一文件到 `src/pages/` 并自动为你创建一个简单的页面。
:::

Astro 将 [`/src/pages/` 目录](/zh-cn/basics/astro-pages/#支持的页面文件) 内的任何受支持的文件视为页面，包括 `.md` 和其他 Markdown 文件类型。

将文件放入此目录或任何子目录中，将使用文件的路径名自动构建页面路由，并显示渲染为 HTML 的 Markdown 内容。Astro 将自动为你的页面添加一个 `<meta charset="utf-8">` 标签，以便更轻松地编写非 ASCII 内容。

```markdown title="src/pages/page-1.md"
---
title: 你好，世界
---

# 嗨！

这个 Markdown 文件在 `your-domain.com/page-1/` 创建了一个页面

也许没有什么样式装点，但 Markdown 还支持这个：
- **加粗** 和 _斜体。_
- 列表项
- [链接](https://astro.build)
- <p>HTML 元素</p>
- 还有更多！
```

### Frontmatter `layout` 属性

为了帮助扩展单个 Markdown 页面的有限功能，Astro 提供了一个特殊的 frontmatter `layout` 属性，它是 Astro [Markdown 布局组件](/zh-cn/basics/layouts/#markdown-布局) 的相对路径。`layout` 不是在使用 [内容集合](/zh-cn/guides/content-collections/) 查询和渲染你的 Markdown 内容时的特殊属性，并且不能保证在其预期用例之外得到支持。

如果你的 Markdown 文件位于 `src/pages/` 中，请创建一个布局组件并将其添加到此布局属性中，来为你的 Markdown 内容提供一个页面外壳。

```markdown title="src/pages/posts/post-1.md" {2}
---
layout: ../../layouts/BlogPostLayout.astro
title: Astro 简介
author: Himanshu
description: 发现 Astro 的魅力之所在！
---
这是一篇使用 Markdown 编写的文章。
```

此布局组件是一个常规 Astro 组件，具有通过 `Astro.props` 为你的 Astro 模板 [自动提供详细的可用属性](/zh-cn/basics/layouts/#markdown-布局的-props) 的功能。例如，你可以通过 `Astro.props.frontmatter` 访问 Markdown 文件的 frontmatter 属性：

```astro title="src/layouts/BlogPostLayout.astro" /frontmatter(?:.\w+)?/
---
const {frontmatter} = Astro.props;
---
<html>
  <head>
    <!-- ... -->
    <meta charset="utf-8"> // no longer added by default
  </head>
  <!-- ... -->
  <h1>{frontmatter.title}</h1>
  <h2>文章作者：{frontmatter.author}</h2>
  <p>{frontmatter.description}</p>
  <slot /> <!-- Markdown 内容被注入到这里 -->
  <!-- ... -->
</html>
```

当使用 frontmatter `layout` 属性时，你必须在布局中包含 `<meta charset="utf-8">` 标签，因为 Astro 不再自动添加它。你现在也可以在布局组件中 [为你的 Markdown 设置样式](/zh-cn/guides/styling/#markdown-样式)。

<ReadMore>阅读更多关于 [Markdown 布局](/zh-cn/basics/layouts/#markdown-布局) 的内容。</ReadMore>

## 请求远程 Markdown

Astro 内置的 Markdown 处理器无法用于处理远程 Markdown 内容。

如需在 [内容集合](/zh-cn/guides/content-collections/) 中使用远程 Markdown，你可以通过 [构建自定义加载器](/zh-cn/guides/content-collections/#构建一个自定义加载器) 来实现，该加载器可调用 [`renderMarkdown()` 函数](/zh-cn/reference/content-loader-reference/#rendermarkdown) 进行内容渲染。

要直接获取远程的 Markdown 并将其渲染为 HTML，你需要从 NPM 安装并配置你自己的 Markdown 解析器。这不会继承你配置的任何 Astro 内置 Markdown 设置。

在你的项目中实现此功能之前，请确保你了解这些限制，并考虑使用内容集合加载器来代替获取远程 Markdown。

```astro title="src/pages/remote-example.astro"
---
// 示例：从一个远程的 API 接口请求 Markdown
// 并在运行时将其渲染为 HTML
// 使用了 "marked" 库 (https://github.com/markedjs/marked)
import { marked } from 'marked';
const response = await fetch('https://raw.githubusercontent.com/wiki/adam-p/markdown-here/Markdown-Cheatsheet.md');
const markdown = await response.text();
const content = marked.parse(markdown);
---
<article set:html={content} />
```
